<?php
/*
This file 'pps_base.php' is part of the PPS project(gather branch) <http://code.google.com/p/fracsnetpps/>

Copyright: (C) 2009 Arthur, B. aka ]{ing <whinemore@gmail.com>

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.
 .
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 .

*/

/* Some quick notes on the new code:
    If a player leaves in the middle of the game they can no longer rating evade
    meaning they will still get -/+ rating when the map changes.
*/
require ('pps_teams.php');

/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////// */
class base_stats{
/*//////////////////////////////////////////////////////////////////////////////////////////////////////////////// */
    
    public $T; /* Teams */
    public $pc; /* Player count, NOTE: unlike T->pc this does not change if the player leaves in the middle of game */
    var $db;
    var $sock;
    var $buffer;
    var $bots;
    private $map_timer;
    private $server_tier;
    private $stmt_player;
    private $player_index; /* By name index of players */
    
    private $leavers; /* Players that left before the map finished */
    private $left_count;
    
	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function __construct( &$database, &$socket, &$buffer, &$statement, $refresh = null ){
	/* ---------------------------------------------------------------------------------------------------------------------- */    
        $this->server_tier = 1;

        /* Varriable Refresh array could be used to set stats */
        /* automatically upon creation of the array              */
        if( isset( $database ) ){

            $this->bots = false;
            $this->db =& $database;
            $this->sock =& $socket;
            $this->buffer =& $buffer;
            
            $this->leavers = array();
            $this->left_count = 0;
            /* Teams */
            $this->T = new teams_container($this->sock);

            $this->stmt_player =& $statement;

            $this->map_timer = time();
            
            $this->pc = 0;
            if( isset( $refresh ) ){
                if( $refresh['players'] ){
        
                    $dublicate = false;
        
                    for( $i = 0; $i < $refresh['players']; $i++ ){                        
                        $name = $refresh['player'][$i]['name'];
                        $id = $refresh['player'][$i]['id'];
                        $team = $refresh['player'][$i]['team'];
                        $player = null;

                        $data = mysqli_query($database, "SELECT * FROM players WHERE ip=\"" . $refresh['player'][$i]['ip'] . "\"");

                        $record = mysqli_fetch_array($data);
                        if( $record ) $player = new base_player( $record, $name ); else $player = new base_player( null, $name );

                        $this->T->add( $name, $team, $id, $player );
                        
                        $this->pc++;
                    }
                }
            }
        }
    }
/* ++++ Ranking functions ++++ */
/* --------------------------------------------------------------------------------------------------------------------------- */    
    private function v($val, $e = 0){ /* 'v' function a simple knock off the one shown in TrueSkill(tm) documentation
/* --------------------------------------------------------------------------------------------------------------------------- */
        if( $val > 0 )
            return (pow(1.2, -($val) * 6));
        else
            return (pow(2, -($val) * 0.25));
    }
/* ---------------------------------------------------------------------------------------------------------------------------- */
    function w($val, $e = 0){ /* 'w' function knock off */
/* ---------------------------------------------------------------------------------------------------------------------------- */
        if( $val == 0 )
            return 0.8;
        if( $val < 0 )
            return -( 1 / pow(1.5, -$val) );    
        
        return ( 1 / pow(1.5, $val * 2) );
    }
	/* ---------------------------------------------------------------------------------------------------------------------------- */
    function c_squared($sigma_w, $sigma_l){ /* Return the value of c squared */
	/* ---------------------------------------------------------------------------------------------------------------------------- */
        return 2 + ($sigma_w * $sigma_w) + ($sigma_l * $sigma_l);
    }
    
	/* ---------------------------------------------------------------------------------------------------------------------------- */
    function newSigma($sigma, $c, $Mu_w, $Mu_l){ /* Get the new sigma for a player */
	/* ---------------------------------------------------------------------------------------------------------------------------- */
        return sqrt(($sigma * $sigma) * (1 - ((($sigma*$sigma)/$c) * $this->w( ($Mu_w - $Mu_l)/sqrt($c) )) ));
    }
    
	/* -------------------------------------------------------------------------------------------------------------------- */
    function newMu_w($sigma, $c, $Mu_w, $Mu_l){ /* Get the new Mu for a player(Winner) */
	/* -------------------------------------------------------------------------------------------------------------------- */
        return $Mu_w + ( (($sigma*$sigma)/sqrt($c)) * $this->v( ($Mu_w - $Mu_l)/sqrt($c) ) );
    }
    
	/* ---------------------------------------------------------------------------------------------------------------------- */
    function newMu_l($sigma, $c, $Mu_w, $Mu_l){ /* Get the new Mu for a player(Looser) */
	/* ----------------------------------------------------------------------------------------------------------------------- */
        return $Mu_l - ( (($sigma*$sigma)/sqrt($c)) * $this->v( ($Mu_w - $Mu_l)/sqrt($c) ) );
    }
       
	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function ch_join($ip){
	/* ---------------------------------------------------------------------------------------------------------------------- */
        $name = substr( $this->buffer ,strpos($this->buffer , "]") + 1);
        $team = (int)substr($this->buffer, strpos($this->buffer, "[") + 1, strpos($this->buffer, "]"));
        $id = (int)substr($this->buffer, 6, strpos($this->buffer, ")"));
        
        $player = new base_player( null, $name );
            
        if( !array_key_exists($name, $this->T->ps) ){
            /* Add a new player */
            $record = null;

            $data = mysqli_query($this->db, "SELECT * FROM players WHERE ip=\"". $ip . "\"");
            if( $data ){ 
                $record = mysqli_fetch_array($data);
            
                if( $record ){ 
                    $player = new base_player( $record, $name );
                }
            }
            
        }
        $player->ip = $ip;
        $this->T->add( $name, $team, $id, $player );
        
        if( count( $this->leavers ) ){
            if( array_key_exists( $name , $this->leavers ) ){
                unset($this->leavers[$name]);
            }elseif( $this->T->pc > 6 ){
                foreach( $this->leavers as $key ){
                    $this->T->remove($key);
                    echo "-> Removing player that left: $key new pc ", $this->T->pc , "\n";
                    unset($this->leavers[$key]);
                }
                $this->leavers = array();
            }
        }
        
        
        echo "$name joined team #$team, new pc = ", $this->T->pc , "\n";
    }
	/* ------------------------------------------------------------------------------------------------------------------- */
    public function ch_left()
	/* ------------------------------------------------------------------------------------------------------------------- */
    {
        $leave_time = time();
        $name = substr( $this->buffer ,strpos($this->buffer , "]") + 1);
        $team = (int)substr($this->buffer, strpos($this->buffer, "[") + 1, strpos($this->buffer, "]"));
        $id = (int)substr($this->buffer, 6, strpos($this->buffer, ")"));
        $p_id = $this->T->ps[$name]->p_id;
        
        foreach( $this->T->ps as $key => $plr )
            $this->T->ps[$key]->dominated[$p_id] = 0;
        
        /* Change this so that players are no longer automatically removed from 
           the array and their stats updated. Instead, place them into a $leavers
           array, where they remain untill the next map, or they rejoin the server(0% done) */
        if( $this->T->ps[$name]->team->number == 5 ){
            $this->T->remove($name);
        }else{
            if( ($leave_time - $this->T->ps[$name]->map_timer) > 180 ){
                /* Simply dont remove players that played over 3 minutes */
                $this->leavers[$name] = $name;
            }else{
                $this->T->remove($name);
            }
        }

        echo "$name left team #$team, new pc = ", $this->T->pc, "\n";
    }

	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function ch_kill(/*void*/){/* Returns false on ahem, false positive.. */
	/* ---------------------------------------------------------------------------------------------------------------------- */
        $loc = (strpos($this->buffer, "with")) + 5;
        $weapon = substr($this->buffer, $loc);
        $kn = substr($this->buffer, 4, strpos($this->buffer, " killed") - 4);
        $vn = substr($this->buffer, strpos($this->buffer, " killed (") + 12, strpos($this->buffer, " with") - (strpos($this->buffer, " killed (") + 12));
        
        //echo $kn , " --- ", $vn , "\n";
        if( !array_key_exists($kn, $this->T->ps) || !array_key_exists($vn, $this->T->ps) ){
            echo "Error killer/victim is not present in system!\n";
            return;
        }
        
        $killer = &$this->T->ps[$kn];
        $victim = &$this->T->ps[$vn];
        
        $killer->update_kills($weapon);

        $victim->d_buffer++;
        
        if( $killer->is_rated ){
            $kill = time();
            $speed = ( $killer->kill_count > 1 ) ? ($killer->kill_count + 1.02) : (2.02);
            if( $kill - $killer->kill_timer < $speed ){
                $killer->kill_count++;
            }else{
                $killer->mlt[$killer->kill_count]++;
                $killer->kill_count = 0;
            }
            
            $killer->kill_timer = $kill;
        }
        
        $killer->dominated[$victim->p_id]++;
        $victim->dominated[$killer->p_id] = 0;
        
        if( $killer->dominated[$victim->p_id] > 4 ){
            $k = ( $killer->acc_name ) ? $killer->acc_name : $kn;
            $d = ( $victim->acc_name ) ? $victim->acc_name : $vn;
            /*switch( $killer->dominated[$victim->p_id] )
            {
                case 5:
                    $domn = "/say $k is harrassing $d...\r\n";
                    socket_write($this->sock, $domn);
                    $killer->dominations++;
                    break;
                case 7:
                    $domn = "/say $k is destroying $d!\r\n";
                    socket_write($this->sock, $domn);
                    $killer->dominations++;
                    break;
                case 9:
                    $domn = "/say $k is DOMINATING $d!!\r\n";
                    socket_write($this->sock, $domn);
                    $killer->dominations++;
                    break;
                case 11:
                    $domn = "/say $k is beating $d like he owes him money..!\r\n";
                    socket_write($this->sock, $domn);
                    $killer->dominations++;
                    break;
                case 20:
                    $domn = "/say Holly shit $k can you leave $d alone?\r\n";
                    socket_write($this->sock, $domn);
                    $domn = "/say I mean really, what has $d ever done to deserve this.\r\n";
                    socket_write($this->sock, $domn);
                    $domn = "/say Youre going to make him quit this game.....\r\n";
                    socket_write($this->sock, $domn);
                    $killer->dominations++;
                    break;
            }*/
       }

    }
	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function ch_grab(/*void*/){
	/* ---------------------------------------------------------------------------------------------------------------------- */
        $grab = substr($this->buffer, 0, strpos($this->buffer, "captured the") - 1);
        $this->T->ps[$grab]->g_buffer++;
    }

	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function ch_cap(/*void*/){
	/* ---------------------------------------------------------------------------------------------------------------------- */
        $name = substr($this->buffer, 0, strpos($this->buffer, "scores for") - 1);
        $this->T->ps[$name]->team->score++; 
        $cteam = $this->T->ps[$name]->team->number;
        
        //echo "INFO: ", $this->buffer , "[$name]($cteam)\n";
        foreach( $this->T->ps as $key => $player ){
            if( $this->T->ps[$key]->team->number == $cteam )
                $this->T->ps[$key]->plus_minus++;
            else
                $this->T->ps[$key]->plus_minus--;
        }
                
        $this->T->ps[$name]->c_buffer++;
    }
	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function ch_return(/*void*/){
	/* ---------------------------------------------------------------------------------------------------------------------- */
        $name = substr($this->buffer, 0, strpos($this->buffer, "returned the") - 1);
        $this->T->ps[$name]->r_buffer++;
    }
	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function ch_nextmap(/*void*/){ /* NOTE: Fromat this function better */
	/* ---------------------------------------------------------------------------------------------------------------------- */
        $winner = 0;
        $final_time = time();
        $full_map = false;
        echo "--> Map finished scores: A: ", $this->T->alpha->score , " / B: ", $this->T->bravo->score , " time:", ($final_time - $this->map_timer), "\n";
        if( $final_time - $this->map_timer < 300 ){
            $this->map_timer = $final_time;
            echo "--> Map Finished in less than 5 min\n";
        }else{
            $full_map = true;
            $this->map_timer = $final_time;
            echo "--> Map Finished normaly\n";
        }
        
        if( $this->T->alpha->score != $this->T->bravo->score ){
            if( $this->T->alpha->score > $this->T->bravo->score ){
                $winner = 1;
            }else{
                $winner = 2;
            }
        }
        $this->T->alpha->score = 0;
        $this->T->bravo->score = 0;  
        
        foreach( $this->T->ps as $key => $player ){
            $this->T->ps[$key]->dominated = array_fill(0, 33, 0);
        }
        
        /* Everything above this line is necessary after each map */     
        if( $this->T->pc < 6 || !$full_map){
            if( count($this->leavers) ){
                foreach( $this->leavers as $key ){
                    
                    $this->T->remove($key);
                    echo "-> Removing player that left: $key new pc ", $this->T->pc , "\n";
                    unset($this->leavers[$key]);
                }
                $this->leavers = array();
            }
            
            return;
        }
        
        $this->update_ratings($winner);
                
        /* Update Rated Players */
        echo "--> UPDATING players in 'ps'\n";
        foreach( $this->T->ps as $name => $player ){

            echo "-> $name ";
            
            $this->T->ps[$name]->merge_buffers();
            if( $winner ){
                $this->T->ps[$name]->maps++;
            }
            
            /* If the player played more than 5 minutes, update their stats with the buffers */
            if( time() - $this->T->ps[$name]->map_timer > 300 ){
                if( !$winner ){
                    echo "tied\n";
                }else{
                    if( $this->T->ps[$name]->team->number == $winner ){
                        echo "won\n";
                        $this->T->ps[$name]->wins++;
                    }elseif( $this->T->ps[$name]->team->number != 5 ){
                        echo "lost\n";
                    }elseif( $this->T->ps[$name]->team->number == 5 ){
                        echo "spectaded\n";
                    }
                }
                
                $this->T->ps[$name]->merge_buffers();
                if( $this->T->ps[$name]->is_rated ){
                    $this->update_player_stats($this->T->ps[$name]);
                    socket_write($this->sock, "PSTAT". $this->T->ps[$name]->auth . "->". round($this->T->ps[$name]->rating,2) ."ENDPSTAT\r\n");
                }
            }else{
                if( $this->T->ps[$name]->team->number != 5 ){
                    echo "dint finish\n";
                }else{
                    echo "spectator\n";
                }
            }
             
             
             $this->T->ps[$name]->map_timer = $final_time;
             $this->T->ps[$name]->clear_buffers();
            
        }   

        if( count($this->leavers) ){
            foreach( $this->leavers as $key ){
                $this->T->remove($key);
                echo "-> Removing player that left: $key new pc ", $this->T->pc , "\n";
                unset($this->leavers[$key]);
            }
            $this->leavers = array();
        }
        return $winner;
    }
	/* ------------------------------------------------------------------------------------------------------------------------------------------------------------------- */
   	private function update_ratings($win_number){
	/* ------------------------------------------------------------------------------------------------------------------------------------------------------------------- */       
        if( $win_number < 3 ){
            if( $win_number == 1 ){
                $win = &$this->T->alpha;
                $loose = &$this->T->bravo;
            }elseif( $win_number == 2){
                $win = &$this->T->bravo;
                $loose = &$this->T->alpha;
            }elseif( !$win_number ){
                return;
            }
        }else{
            return;
        }
        if( (!$win->mu || !$loose->mu) ){
            return null;
        }
        
        $L = array();
        $l_sigma = $loose->sigma;
        $l_mu = $loose->mu;
        
        $W = array();
        $w_sigma = $win->sigma;
        $w_mu = $win->mu;
        
        $l_pc = $loose->count;
        $w_pc = $win->count;
        
        /* Generate temporary teams */
        foreach( $this->T->ps as $name => $player ){
            if( $this->T->ps[$name]->is_rated ){
                if( $this->T->ps[$name]->team->number == $win->number){
                    $W[$name] = $this->T->ps[$name];
                }elseif( $this->T->ps[$name]->team->number == $loose->number ){
                    $L[$name] = $this->T->ps[$name];
                }
            }
        }

        foreach( $W as $name => $value ){
            
            $c = $this->c_squared( $W[$name]->sigma, ($l_sigma/$l_pc) );
            $sigma = 0;
            
            $sigma = $this->newSigma( $W[$name]->sigma,
                                      $c,
                                      $W[$name]->mu,
                                      ($l_mu/$l_pc) );
                                                          
            $W[$name]->mu = $this->newMu_w( $sigma,
                                            $c,
                                            $W[$name]->mu,
                                            ($l_mu/$l_pc) );
            
            $W[$name]->sigma = $sigma;
            $W[$name]->old_rating = $W[$name]->rating;
            $W[$name]->rating = $W[$name]->mu - ( $W[$name]->sigma * 2.5);
            
            $this->T->ps[$name] = $W[$name];
        }
        foreach( $L as $name => $value ){

            $c = $this->c_squared( ($w_sigma/$w_pc), $L[$name]->sigma );
            $sigma = 0;

            $sigma = $this->newSigma( $L[$name]->sigma, 
                                      $c, 
                                      ($w_mu/$w_pc), 
                                      $L[$name]->mu );
                                      
            $L[$name]->mu = $this->newMu_l( $sigma, 
                                            $c, 
                                            ($w_mu/$w_pc), 
                                            $L[$name]->mu );
                                            
            $L[$name]->sigma = $sigma;
            $L[$name]->old_rating = $L[$name]->rating;
            $L[$name]->rating = $L[$name]->mu - ($L[$name]->sigma * 2.5);
            
            $this->T->ps[$name] = $L[$name];
        }           
            
    }
	/* ---------------------------------------------------------------------------------------------------------------------- */
    public function update_player_stats($player_pointer)
	/* ---------------------------------------------------------------------------------------------------------------------- */
    {
        /* First Part Update Player specific stats */
        $P = &$player_pointer;
        $P->update_kdr();
        $P->update_cgr();
        $P->update_ckr();
        $P->update_time_played();
        
        $this->stmt_player->bind_param('iiiiiiiiddddiiiiiiiiiiiiiiiiiiiddids', 
                                        $P->k,
                                        $P->d,
                                        $P->mlt[1],
                                        $P->mlt[2],
                                        $P->mlt[3],
                                        $P->c,
                                        $P->g,
                                        $P->r,
                                        $P->kdr,
                                        $P->cgr,
                                        $P->ckr,
                                        $P->rating,
                                        $P->wins,
                                        $P->maps,
                                        $P->tp,
                                        $P->w['Desert Eagles'], 
                                        $P->w['HK MP5'], 
                                        $P->w['Ak-74'],
                                        $P->w['Steyr AUG'],
                                        $P->w['Spas-12'],
                                        $P->w['Ruger 77'],
                                        $P->w['M79'],
                                        $P->w['Barrett M82A1'],
                                        $P->w['FN Minimi'],
                                        $P->w['Selfkill'],
                                        $P->w['Combat Knife'],
                                        $P->w['Chainsaw'],
                                        $P->w['LAW'],
                                        $P->w['Grenade'],
                                        $P->wstreak,
                                        $P->plus_minus,
                                        $P->mu,
                                        $P->sigma,
                                        $P->dominations,
                                        $P->old_rating,
                                        $P->ip);
                                                                
        $this->stmt_player->execute() or die("Could Not Execute Prepared Statement\n");
                            
        foreach ($P->w as $key => $weapon )
        {
            if( $weapon ){
                $query = "UPDATE weapons SET kills=kills+" . $weapon . " WHERE weapon = \"" . $key . "\"";
                mysqli_query($this->db, $query);
            }
            $P->w[$key] = 0;
        } 

    }
}
